/*
 * message.c
 *
 *  Created on: 05/11/2012
 *      Author: julian
 */

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "cli_message_def.h"
#include "cli_message.h"
#include "socket_interface.h"

int _s;
node_t node_ids;

int msgSvcInit() {

    _s = openSocket();

    if (_s < 0) {
#ifdef USR_MSG_DEBUG
        perror("MsgSvcInit.openSocket.Error iniciando socket");
#endif
        return (-1);
    }

    node_ids.list.next = 0;
    node_ids.list.prev = 0;

    if (requestNodeList() == -1) {
        return (-1);
    }

    return (0);

}

int msgSvcExit() {

    if (close(_s) < 0) {
#ifdef USR_MSG_DEBUG
        perror("MsgSvcExit.close.Error al cerrar socket");
#endif
        return (-1);
    } else {
        struct list_head *pos, *q;
        node_t *tmp;

        if (node_ids.list.next != 0 && node_ids.list.prev != 0) {
            list_for_each_safe(pos, q, &node_ids.list) {
                tmp = list_entry(pos, node_t, list);
                list_del(pos);
                free(tmp);
            }
#ifdef USR_MSG_DEBUG
            printf("\nExit borrando lista de nodos.");
#endif
        }
        return (0);
    }
}

void print_msg_t(struct msg_t msg) {

    uint8_t i = 0;
    printf("\n msg:opts: %X ", msg.options);
    printf("action: %X ", msg.type);
    printf("len: %d ", msg.len);
    printf("nodeId: %s ", msg.nodeId);
    printf("data: ");
    for (i = 0; i < msg.len; i++) {
        printf("%X", msg.data[i]);
    }
    printf("\n");
}

int sendMessage(struct msg_t msg) {

    int r = -2;

#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMessage.socketSend.Error enviando al socket");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf("sent --> %d\n", r);
#endif
    int ret = -2;
    if (socketReceive(_s, &ret, sizeof(int), NULL, 0,
            SEND_MSG_RETURN_TIMEOUT_SEC, SEND_MSG_RETURN_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMessage.recvfrom.Error recibiendo del socket");
#endif
        flushSocket(_s);
        return (-1);
    }

    if (ret!=0) {
        flushSocket(_s);
    }

#ifdef USR_MSG_DEBUG
    printf("sendM ret--> %d\n", ret);
#endif
    return ret;
}

int sendMulticast(struct msg_t msg, dest_list_t *list) {

    int r = -2;
    uint8_t i;

    if (list == NULL) {
        errno = EINVAL;
        return (-1);
    }

    msg.options |= MULTICAST_BIT;
    memset(&msg.nodeId, 0, 20);
    msg.nodeId[0] = list->size;

#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMulticast.socketSend.Error enviando al socket");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf(" sent--> %d\n", r);
#endif

    for (i = 0; i < list->size; i++) {

        r = socketSend(_s, list->id_list[i], 20, NULL, 0,
                0, SOCKET_SEND_TIMEOUT_MSEC);
        if (r < 0) {
#ifdef USR_MSG_DEBUG
            perror("sendMulticast.socketSend.Error enviando al socket");
            return (-1);
#endif
        }

#ifdef USR_MSG_DEBUG
        printf(" sent--> %d ", r);
        printf("id--> %s\n", list->id_list[i]);
#endif
    }

    int ret = -2;

    if (socketReceive(_s, &ret, sizeof(int), NULL, 0,
            SEND_MULTICAST_RETURN_TIMEOUT_SEC,
            SEND_MULTICAST_RETURN_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMulticast.socketReceive.Error recibiendo del socket");
#endif
        return (-1);
    }

#ifdef USR_MSG_DEBUG
    printf("multicast ret--> %d\n", ret);
#endif
    return ret;

}

int requestNodeList() {

    int r = -2, size = -1;
    struct msg_t msg;
    uint8_t i = 0;

    msg.options = 0;
    memset(&msg.nodeId, 0, 20);
    msg.len = 0;
    msg.type = NODE_LIST;
#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("requestNodeList.socketSend.Error enviando al socket");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf(" sent--> %d\n", r);
#endif

    r = socketReceive(_s, &size, sizeof(int), NULL, 0,
            NODE_LIST_REQUEST_TIMEOUT_SEC, NODE_LIST_REQUEST_TIMEOUT_MSEC);
    if (r < 0) {
        if (r == -1) {
#ifdef USR_MSG_DEBUG
            perror("requestNodeList.socketReceive.Error recibiendo del socket");
#endif
            return (-1);
        }
    }

    if (size <= 0) {
        fprintf(stderr, "\nNo nodes received.\n");
        return 0;
    }
    printf(" %d nodes.\n", size);

    struct list_head *pos, *q;
    node_t *tmp;

    if (node_ids.list.next != 0 && node_ids.list.prev != 0) {
        list_for_each_safe(pos, q, &node_ids.list) {
            tmp = list_entry(pos, node_t, list);
            list_del(pos);
            free(tmp);
        }
#ifdef USR_MSG_DEBUG
        printf("\nrequestNodeList borrando la lista de nodos.");
#endif
    }

    INIT_LIST_HEAD(&node_ids.list);
    node_t *j;

    char buf[20];

    for (i = 0; i < size; i++) {

        if (socketReceive(_s, buf, sizeof(buf), NULL, 0, 0,
                SOCKET_RCV_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
            perror("requestNodeList.socketReceive.Error recibiendo del socket");
#endif
            return (-1);
        } else {
            j = (node_t*) malloc(sizeof(node_t));
            strcpy(j->id, buf);
            list_add(&(j->list), &(node_ids.list));
        }
    }

    list_for_each(pos, &node_ids.list) {
        j = list_entry(pos,node_t, list);
        printf("id: %s\n", j->id);
    }

    return (0);
}

int updateNodeList() {

    int r = -2, size = -1;
    struct msg_t msg;
    uint8_t i = 0;

    msg.options = 0;
    memset(&msg.nodeId, 0, 20);
    msg.len = 0;
    msg.type = NODE_LIST_UPDATE;
#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("updateNodeList.socketSend.Error enviando al socket");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf(" sent--> %d\n", r);
#endif

    r = socketReceive(_s, &size, sizeof(int), NULL, 0,
            NODE_LIST_UPDATE_TIMEOUT_SEC, 0);
    if (r < 0) {
        if (r == -1) {
#ifdef USR_MSG_DEBUG
            perror("updateNodeList.socketReceive.Error recibiendo del socket");
#endif
            return (-1);
        }
        size = 0;
    }

    printf(" %d nodes.\n", size);

    if (size <= 0) {
        fprintf(stderr, "\nNo nodes received.\n");
    }

    struct list_head *pos, *q;
    node_t *tmp;

    if (node_ids.list.next != 0 && node_ids.list.prev != 0) {
        printf("\nupdate is deleting list.");
        list_for_each_safe(pos, q, &node_ids.list) {
            tmp = list_entry(pos, node_t, list);
            list_del(pos);
            free(tmp);
        }
    }

    INIT_LIST_HEAD(&node_ids.list);
    node_t *j;

    char buf[20];

    for (i = 0; i < size; i++) {

        if (socketReceive(_s, buf, sizeof(buf), NULL, 0, 0,
                SOCKET_RCV_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
            perror("updateNodeList.socketReceive.Error recibiendo del socket");
#endif
            return (-1);
        } else {
            j = (node_t*) malloc(sizeof(node_t));
            strcpy(j->id, buf);
            list_add(&(j->list), &(node_ids.list));
        }
    }

    list_for_each(pos, &node_ids.list) {
        j = list_entry(pos,node_t, list);
        printf("id: %s\n", j->id);
    }

    return (0);
}

uint8_t * getResponse() {

    uint8_t *response = NULL;
    int r = -2;
    struct msg_t msg;

    if (socketReceive(_s, &msg, sizeof(struct msg_t), NULL, 0,
            GET_RESPONSE_TIMEOUT_SEC, GET_RESPONSE_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
        perror("getResponse.socketReceive.Error recibiendo del socket");
#endif
        flushSocket(_s);
        return NULL;
    }

    if (msg.len == 0) {
#ifdef USR_MSG_DEBUG
        fprintf(stderr, "\nmsg_len=0 ");
        print_msg_t(msg);
#endif
        flushSocket(_s);
        return NULL;
    }

    response = malloc(msg.len * sizeof(uint8_t));
    for (r = 0; r < msg.len; r++) {
        response[r] = msg.data[r];
    }

#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
    printf("\nresponse: ");
    for (r = 0; r < msg.len; r++) {
        printf("%X ", response[r]);
    }
#endif

    return response;

}

int msgOutput(struct msg_t msg, dest_list_t* list) {

    if (list != NULL) {

        if (msg.options & RESPONSE_REQUIRED_BIT) {
#ifdef USR_MSG_DEBUG
            fprintf(stderr, "Solo en unicast.\n");
#endif
            errno = ENOTSUP;
            return (-1);
        }

        if (sendMulticast(msg, list) < 0) {
            errno = EPROTO;
            return -1;
        }

    } else {

        if ((strcmp(msg.nodeId, BROADCAST_ID) == 0)
                && (msg.options & RESPONSE_REQUIRED_BIT)) {
#ifdef USR_MSG_DEBUG
            fprintf(stderr, "Solo en unicast.\n");
#endif
            errno = ENOTSUP;
            return (-1);
        }

        if (sendMessage(msg) < 0) {
            errno = EPROTO;
            return -1;
        }

    }

    return 0;

}

